#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<assert.h>
#include<stdlib.h>
#include<ctype.h>
#include<fcntl.h>


#define LEFT "["
#define RIGHT "]"
#define LABLE "$"

#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 44
#define EXIT_CODE 0

#define NONE -1
#define IN_RDIR 0
#define OUT_RDIR 1
#define APPEND_RDIR 0

extern char **environ;
int quit=0;
int lastcode=0;
char commandline[LINE_SIZE];
char pwd[LINE_SIZE];
char *argv[ARGC_SIZE];
char* rdirfilename=NULL;
int rdir=NONE;

char*cmds[ARGC_SIZE];
char myenv[LINE_SIZE];

//getusrname
char* GetUsrName()
{
    return getenv("USER");
}

//gethostname
char* GetHostName()
{
    return getenv("HOSTNAME");
}

//getpid
void Getpwd()
{
    getcwd(pwd,sizeof(pwd));
}

void check_redir(char*cmd)
{
    //ls -al -n
    //ls -al -n >/</>> filename.txt
    char *pos = cmd;
    while (*pos)
    {
        if (*pos == '>')
        {
            if(*(pos+1)=='>')
            {
                *pos++='\0';
                *pos++='\0';
                while(isspace(*pos)) pos++;
                rdirfilename=pos;
                rdir=APPEND_RDIR;
                break;
            }
            else
            {
                *pos++='\0';
                while(isspace(*pos)) pos++;
                rdirfilename=pos;
                rdir=OUT_RDIR;
                break;
            }
        }
        else if(*pos=='<')
        {
            *pos++='\0';
            while(isspace(*pos)) pos++;
            rdirfilename=pos;
            rdir=IN_RDIR;
            break;
        }
        else
        {
            //do nothing
        }
        pos++;
    }
}

void interact(char* cline,int size)
{
    Getpwd();
    printf(LEFT"%s@%s %s"RIGHT LABLE" ",GetUsrName(),GetHostName(),pwd);
    char*s=fgets(cline,size,stdin);
    assert(s);
    (void)s;
    cline[strlen(cline)-1]='\0';    
    // check_redir(cline);
}
int splitstring(char cline[],char*_argv[])
{
    int i=0;
    //第一次分割，从cline的开头开始找分割符，返回第一个字串的指针
    _argv[i++]=strtok(cline,DELIM);
    while(_argv[i++]=strtok(NULL,DELIM));
    return i-1;
}

int builtcommand(char*_argv[],int _argc)
{
    if(_argc==2 &&strcmp(_argv[0],"cd")==0)
    {
        chdir(_argv[1]);
        Getpwd();
        sprintf(getenv("PWD"),"%s",pwd);
        return 1;
    }
    else if(_argc==2 &&strcmp(_argv[0],"export")==0)
    {
        strcpy(myenv,_argv[1]);
        putenv(myenv);
        return 1;
    }
    else if(_argc==2 &&strcmp(_argv[0],"echo")==0)
    {
        if(strcmp(_argv[1],"$?")==0)
        {
            printf("%d",lastcode);
            lastcode=0;
        }
        // else if(strcmp(*_argv[1],'$')==0)
        else if(_argv[1]!=NULL&&_argv[1][0]=='$')
        {
            char*s=getenv(_argv[1]+1);
            if(s)
            {
                printf("%s\n",s);
            }
        }
        else
        {
            printf("%s\n",_argv[1]);
        }
        return 1;
    }
    if(strcmp(_argv[0],"ls")==0)
    {
        _argv[_argc++]="--color";
        _argv[_argc]=NULL;
    }
    return 0;
}

void NormalExcute(char*_argv[])
{
    pid_t id=fork();
    if(id<0)
    {
        perror("fork");
        return;
    }
    else if(id==0)
    {
        // //让子进程执行命令
        //重定向
        int fd=0;
        if(rdir==IN_RDIR)
        {
            fd=open(rdirfilename,O_RDONLY,0666);
            dup2(fd,0);
        }
        else if(rdir==OUT_RDIR)
        {
            fd=open(rdirfilename,O_CREAT|O_WRONLY|O_TRUNC,0666);
            if(fd<0)
            {
                perror("open");
                exit(EXIT_FAILURE);
            }
            dup2(fd,1);
        }
        else if(rdir==APPEND_RDIR)
        {
            fd=open(rdirfilename,O_CREAT|O_WRONLY|O_APPEND,0666);
            dup2(fd,1);
        } 
        execvp(_argv[0],_argv);
        exit(EXIT_CODE);
    }
    else
    {
        int status=0;
        pid_t rid=waitpid(id,&status,0);
        if(rid==id)
        {
            lastcode=WEXITSTATUS(status);
        }
    }

}



// //解析命令行中的管道,拆分多个命令
// //ls -l | grep .log  -->"ls -l"  "grep .log"
// //返回得到子命令的数量
// int prase_pipeline(char*cline,char**_cmds)
// {
//     int num_cmds=0;
//     char*token=strtok(cline,"|");
//     while(token !=NULL&&num_cmds<ARGC_SIZE)
//     {
//         //去除命令前后的空格
//         //跳过开头的空白
//         while(*token==' '||*token=='\t') token++;
//         //找到字符串末尾,往前跳过末尾的空白
//         char*end=token+strlen(token)-1;
//         while(end>token &&(*end==' '||*end=='\t')) end--;
//         //字符串末尾空白得到位置截断,让子命令字符更整洁
//         *(end+1)='\0';
//         cmds[num_cmds++]=token;
//         token=strtok(NULL,"|");
//     }
//     //按照execvp等函数的要求,数组最后一个元素为空
//     cmds[num_cmds]=NULL;
//     return num_cmds;
// }

// int prase_pipeline(char*cline,char**_cmds)
// {
//     int num_cmd = 0;
//     char *token = strtok(cline, "|");
//     while (*token != NULL && num_cmd < ARGC_SIZE)
//     {
//         while (*token == ' ' || *token == '\t') token++;
//         char*end=token+strlen(token)-1;
//         while(end>token &&(*end==' '||*end=='\t'))end--;
//         *(end+1)='\0';
//         _cmds[num_cmd++]=token;
//         token=strtok(NULL,"|");
//     }
//     _cmds[num_cmd]=NULL;
//     return num_cmd;
// }

//执行管道的命令
void  execute_pipeline(char* commands[],int num_cmds)
{
    int pipefds[2*(num_cmds-1)];
    pid_t pids[num_cmds];
    //创建所以需要创建的管道
    for(int i=0;i<num_cmds-1;i++)
    {
        if(pipe(pipefds+i*2)==-1)
        {
            perror("pipe");
            exit(EXIT_FAILURE);
        }
    }
    //让每个命令创建子进程并执行
    for(int i=0;i<num_cmds;i++)
    {
        pids[i]=fork();
        if(pids[i]==-1)
        {
            perror("fork");
            exit(EXIT_FAILURE);
        }
        
        if(pids[i]==0) //child
        {
            for(int j=0;j<2*(num_cmds-1);j++)
            {
                if(!((i>0 &&j==(i-1)*2)||(i<num_cmds-1&&j==i*2+1)))
                    close(pipefds[j]);
            }
            //不是第一个命令：从管道读取输入（重定向stdin）
            if(i>0)
            {
               int fd1= dup2(pipefds[(i-1)*2],STDIN_FILENO);
               
                if(fd1==-1)
                {
                    printf("%d",fd1);
                    perror("dup2 fail(STD_IN)");
                    exit(EXIT_FAILURE);
                }
            }
            //不是最后一个命令：输出到管道（重定向stdout）
            if(i<num_cmds-1)
            {
                int fd2=dup2(pipefds[i*2+1],STDOUT_FILENO);
                if(fd2==-1)
                {
                    perror("dup2 fail(STD_OUT)");
                    exit(EXIT_FAILURE);
                }
            }
            // //关闭所以通道（子进程不需要的管道描述符）
            // for(int j=0;j<2*(num_cmds-1);j++)
            // {
            //     close(pipefds[j]);
            // }
            char*cmd=commands[i];
            rdirfilename=NULL;
            rdir=NONE;
            check_redir(cmd);
            if(rdir!=NONE)
            {
                int fd;
                if(rdir==IN_RDIR)
                {
                    fd=open(rdirfilename,O_RDONLY);
                }
                else if(rdir==OUT_RDIR)
                {
                    fd=open(rdirfilename,O_CREAT|O_WRONLY|O_TRUNC,0666);
                }
                else
                {
                    fd=open(rdirfilename,O_CREAT|O_WRONLY|O_APPEND,0666);
                }
                
                if(fd==-1)
                {
                    perror("open rdir");
                    exit(EXIT_FAILURE);
                }
                dup2(fd,(rdir==IN_RDIR)?STDIN_FILENO:STDOUT_FILENO);
                close(fd);
            }
            char*args[ARGC_SIZE];
            int argc=splitstring(commands[i],args);
            //处理内置命令
            if(builtcommand(args,argc))
            {
                exit(EXIT_CODE);
            }
            //处理普通命令
            if(execvp(args[0],args)==-1)
            {
                perror("execvp");
                exit(EXIT_FAILURE);
            }
        }
    }
        //father
        {
            //父进程关闭所有的管道
            for(int i=0;i<2*(num_cmds-1);i++)
            {
                close(pipefds[i]);
            }
            //父进程等待所有的子进程执行完毕
            for(int i=0;i<num_cmds;i++)
            {
                waitpid(pids[i],NULL,0);
            }
        }
    
}

//解析命令行中的管道,拆分多个命令
//ls -l | grep .log  -->"ls -l"  "grep .log"
//返回得到子命令的数量
int prase_pipeline(char*cline,char**_cmds)
{
    int num_cmds=0;
    char*token=strtok(cline,"|");
    while(token !=NULL&&num_cmds<ARGC_SIZE)
    {
        //去除命令前后的空格
        //跳过开头的空白
        while(*token==' '||*token=='\t') token++;
        //找到字符串末尾,往前跳过末尾的空白
        char*end=token+strlen(token)-1;
        while(end>token &&(*end==' '||*end=='\t')) end--;
        //字符串末尾空白得到位置截断,让子命令字符更整洁
        *(end+1)='\0';
        cmds[num_cmds++]=token;
        token=strtok(NULL,"|");
    }
    //按照execvp等函数的要求,数组最后一个元素为空
    cmds[num_cmds]=NULL;
    return num_cmds;
}

int main()
{
    while (!quit)
    {
        // 1.
        // 2.交互问题，获取命令行 ls -a -l
        interact(commandline, sizeof(commandline));
        //解析包含管道符号|的命令，将其分为多个独立的命令字符串
        int num_cmds = prase_pipeline(commandline, cmds);
        if (num_cmds > 1)
        {
            // 负责创建管道,创建子进程,并将每个命令与相应的管道进行连接,实现命令顺序通过管道进行数据传递和执行
            execute_pipeline(cmds, num_cmds);
        }
        else if (num_cmds == 1)
        {
            // 3.字串分割问题 ls -a -l --> "ls" "-a" "-l"
            int argc = splitstring(commandline, argv);
            if (argc == 0)
                continue;
            // 4.指令的判断，内键命令-本质上是shell内部的一个函数
            int n = builtcommand(argv, argc);
            // 执行普通命令
            if (!n)
                NormalExcute(argv);
        }
    }
    return 0;
}